# @Time : 2021/4/3 9:33
# @Author : Fioman 
# @Phone : 13149920693
"""
1. 模块说明: 上料视觉控制卡驱动模块
2. 模块功能: 主要用来和上料视觉控制卡进行通信
"""
from ctypes import *

import gevent

from db_tools.db_handler import DbHandler
from settings import ZMC_LIB_PATH, AUX_ZMC_LIB_PATH
from tools.hd_logger import HdLogger

camZmcParamsKey = [
    "Xwidth",  # 轴有效行程  mm :0
    "Xunits",  # 轴当量，单位 p/mm :2 导程25，无减速机，电子齿轮比25,
    "XminSpeed",  # 轴最小速度,单位 mm/s :4
    "XmaxSpeed",  # 轴最大速度，单位 mm/s :6
    "XworkingSpeed",  # 轴工作速度，单位 mm/s :8
    "Xaccel",  # 轴加速度，单位 mm/s2 :10
    "Xdecel",  # 轴减速度，单位 mm/s2 :12
    "Xsramp",  # 加减速过程S曲线设置，单位ms :14
    "XhomeSpeed",  # 轴回零速度，单位 mm/s :16
    "Xhomeaccel",  # 轴回零加速度，单位 mm/s2 :18
    "Xhomedecel",  # 轴回零减速度，单位 mm/s2 :20

    "YReducerRatio",  # 轴减速比 :22
    "YElecGearRatio",  # 轴电子齿轮比  :24
    "ENcoderR",  # 轴编码器分辨率  :26
    "transporterRH",  # 轴滚筒直径  :28
    "transporterPH",  # 轴皮带厚度  :30
    "Yunits",  # 轴当量，单位 p/mm  :32
    "YminSpeed",  # 轴最小速度,单位 mm/s :34
    "YmaxSpeed",  # 轴最大速度，单位 mm/s :36
    "YworkingSpeed",  # 轴工作速度，单位 mm/s :38
    "Yaccel",  # 轴加速度，单位 mm/s2 :40
    "Ydecel",  # 轴减速度，单位 mm/s2 :42
    "Ysramp",  # 加减速过程S曲线设置，单位ms :44
    "YhomeSpeed",  # 轴回零速度，单位 mm/s :46
    "Yhomeaccel",  # 轴回零加速度，单位 mm/s2 :48
    "Yhomedecel",  # 轴回零减速度，单位 mm/s2 :50
    "YreworkingSpeed",  # 重拍速度，单位 mm/s :52
    "YreworkingAccel",  # 重拍加速度，单位 mm/s2 :54
    "YreworkingDecel",  # 重拍减速度，单位 mm/s2 :56

    "Zwidth",  # 轴有效行程 mm :58
    "Zunits",  # 轴当量，单位 p/mm :60,导程20
    "ZminSpeed",  # 轴最小速度,单位 mm/s :62
    "ZmaxSpeed",  # 轴最大速度，单位 mm/s :64
    "ZworkingSpeed",  # 轴工作速度，单位 mm/s :66
    "Zaccel",  # 轴加速度，单位 mm/s2 :68
    "Zdecel",  # 轴减速度，单位 mm/s2 :70
    "Zsramp",  # 加减速过程S曲线设置，单位ms :72
    "ZhomeSpeed",  # 轴回零速度，单位 mm/s :74
    "Zhomeaccel",  # 轴回零加速度，单位 mm/s2 :76
    "Zhomedecel",  # 轴回零减速度，单位 mm/s2 :78

    "Rwidth",  # 轴有效行程 mm :80
    "Runits",  # 轴当量，单位 p/mm :82
    "RminSpeed",  # 轴最小速度,单位 mm/s :84
    "RmaxSpeed",  # 轴最大速度，单位 mm/s :86
    "RworkingSpeed",  # 轴工作速度，单位 mm/s :88
    "Raccel",  # 轴加速度，单位 mm/s2 :90
    "Rdecel",  # 轴减速度，单位 mm/s2 :92
    "Rsramp",  # 加减速过程S曲线设置，单位ms :94
    "RhomeSpeed",  # 轴回零速度，单位 mm/s :96
    "Rhomeaccel",  # 轴回零加速度，单位 mm/s2 :98
    "Rhomedecel",  # 轴回零减速度，单位 mm/s2 :100
]


class CamZmc(object):
    """
    拍照控制器驱动类
    """

    def __init__(self):
        self.handleRobot = c_longlong()
        self.logger = HdLogger()
        self.errorMsg = ""
        self.tryAgainDuraion = 0.5  # 通信失败,重新试一遍的时间间隔
        self.ip = "192.168.0.11"
        self.db = DbHandler()
        self.tableName = "camZmcParams"
        try:
            self.api = cdll.LoadLibrary(ZMC_LIB_PATH)
            self.aux_api = cdll.LoadLibrary(AUX_ZMC_LIB_PATH)
        except Exception as e:
            self.logger.debug("in CamZmc.__init__() error : {}".format(str(e)))
            self.api = None
            self.aux_api = None

    def __del__(self):
        """
        对象被销毁的时候调用次方法.
        :return:
        """
        try:
            self.close_zmc_conn()
        except Exception as e:
            errorInfo = "in CamZmc.__init__() error : {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)

    def check_connect(self):
        try:
            isConnect = self.get_modbus_bit(0, 1)[0]
            isConnect = True if isConnect == 0 else False
            return isConnect
        except Exception as e:
            errorInfo = "in CamZmc.check_connect() error : {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)
            return False

    def cam_zmc_start(self):
        """
        拍照控制器启动
        :return:
        """
        bitPos = 0
        bitValue = 1
        ret = self.set_modbus_bit(bitPos, bitValue)
        if ret != 0:
            gevent.sleep(self.tryAgainDuraion)
            ret = self.set_modbus_bit(bitPos, bitValue)
            if ret != 0:
                self.errorMsg = "in cam_zmc_start() 设置 modbus_bit({}) = {} 失败. ".format(bitPos, bitValue)
            return self.errorMsg
        return ret

    def cam_zmc_stop(self):
        """
        拍照控制器停止
        :return:
        """
        bitPos = 1
        bitValue = 1
        ret = self.set_modbus_bit(bitPos, bitValue)
        if ret != 0:
            # 重新试一次
            gevent.sleep(self.tryAgainDuraion)
            ret = self.set_modbus_bit(bitPos, bitValue)
            if ret != 0:
                self.errorMsg = "in cam_zmc_stop() 设置 modbus_bit({}) = {} 失败".format(bitPos, bitValue)
                return self.errorMsg
        return ret

    def cam_zmc_rs_stop(self):
        """
        拍照控制器设备急停
        :return:
        """
        bitPos = 2
        bitValue = 1
        ret = self.set_modbus_bit(bitPos, bitValue)
        if ret != 0:
            # 重新试一次
            gevent.sleep(self.tryAgainDuraion)
            ret = self.set_modbus_bit(bitPos, bitValue)
            if ret != 0:
                self.errorMsg = "in cam_zmc_rs_stop() 设置 modbus_bit({}) = {} 失败".format(bitPos, bitValue)
                return self.errorMsg
        return ret

    def cam_zmc_reset(self):
        """
        拍照控制器复位
        :return:
        """
        bitPos = 3
        bitValue = 1
        ret = self.set_modbus_bit(bitPos, bitValue)
        if ret != 0:
            # 重新试一次
            gevent.sleep(self.tryAgainDuraion)
            ret = self.set_modbus_bit(bitPos, bitValue)
            if ret != 0:
                self.errorMsg = "in cam_zmc_reset() 设置 modbus_bit({}) = {} 失败!".format(bitPos, bitValue)
                return self.errorMsg
        return ret

    def get_allow_get_pic_state(self):
        """
        上料控制器是否允许拍照的状态,1表示允许拍照
        :return: 1.通信状态 0表示成功,其他表示失败   2. 读取到的值,0后者1,1表示允许拍照
        """
        bitPos = 15
        bitCounts = 1
        ret, val = self.get_modbus_bit(bitPos, bitCounts)
        if ret != 0:
            # 重新试一次
            gevent.sleep(0.1)
            ret, val = self.get_modbus_bit(bitPos, bitCounts)
            if ret != 0:
                self.errorMsg = "in get_allow_pic_state() 获取 modbus_bit({}) 失败!".format(bitPos)
                return -1, self.errorMsg
        return 0, val[0]

    def clear_allow_get_pic_state(self):
        """
        清楚允许拍照的信号,获取到之后马上清楚
        :return:
        """
        bitPos = 15
        bitValue = 0
        ret = self.set_modbus_bit(bitPos, bitValue)
        if ret != 0:
            # 重新试一次
            gevent.sleep(self.tryAgainDuraion)
            ret = self.set_modbus_bit(bitPos, bitValue)
            if ret != 0:
                self.errorMsg = "in clear_allow_get_pic_state() 设置 modbus_bit ({}) = {} 失败".format(bitPos, bitValue)
                return self.errorMsg
        return ret

    def set_allow_take_pic(self):
        """
        设置上料控制允许拍照
        :return:
        """
        bitPos = 16
        bitValue = 1
        ret = self.set_modbus_bit(bitPos, bitValue)
        if ret != 0:
            # 重新试一次
            gevent.sleep(self.tryAgainDuraion)
            ret = self.set_modbus_bit(bitPos, bitValue)
            if ret != 0:
                self.errorMsg = "in set_allow_take_pic() 设置 modbus_bit ({}) = {} 失败".format(bitPos, bitValue)
                return self.errorMsg
        return ret

    def img_result_ok(self):
        """
        设置上料控制拍照结果ok
        :return:
        """
        bitPos = 19
        bitValue = 1
        ret = self.set_modbus_bit(bitPos, bitValue)
        if ret != 0:
            # 重新试一次
            gevent.sleep(self.tryAgainDuraion)
            ret = self.set_modbus_bit(bitPos, bitValue)
            if ret != 0:
                self.errorMsg = "in img_result_ok() 设置 modbus_bit ({}) = {} 失败".format(bitPos, bitValue)
                return self.errorMsg
        return ret

    def clear_img_result_failed(self):
        """
        清除拍照失败的NG信号,每次拍照的时候,这个地方都清除一下,防止底层没有清除掉
        :return:
        """
        bitPos = 20
        bitValue = 0
        ret = self.set_modbus_bit(bitPos, bitValue)
        if ret != 0:
            # 重新试一次
            gevent.sleep(self.tryAgainDuraion)
            ret = self.set_modbus_bit(bitPos, bitValue)
            if ret != 0:
                self.errorMsg = "in clear_img_result_failed() 设置 modbus_bit ({}) = {} 失败".format(bitPos, bitValue)
                return self.errorMsg

    def img_result_failed(self):
        """
        设置上料控制拍照结果NG
        :return:
        """
        bitPos = 20
        bitValue = 1
        ret = self.set_modbus_bit(bitPos, bitValue)
        if ret != 0:
            # 重新试一次
            gevent.sleep(self.tryAgainDuraion)
            ret = self.set_modbus_bit(bitPos, bitValue)
            if ret != 0:
                self.errorMsg = "in img_result_failed() 设置 modbus_bit ({}) = {} 失败".format(bitPos, bitValue)
                return self.errorMsg
        return ret

    def is_running(self):
        """
        获取上料控制器是否是运行状态
        :return:
        """
        bitPos = 100
        bitCounts = 1
        ret, val = self.get_modbus_bit(bitPos, bitCounts)
        if ret != 0:
            # 重新试一次
            gevent.sleep(0.1)
            ret, val = self.get_modbus_bit(bitPos, bitCounts)
            if ret != 0:
                self.errorMsg = "in is_running() 读取 modbus_bit ({}) 失败".format(bitPos)
                return -1, self.errorMsg
        return 0, val[0]

    def is_stop_state(self):
        """
        获取上料控制器是否是停止状态
        :return:
        """
        bitPos = 101
        bitCounts = 1
        ret, val = self.get_modbus_bit(bitPos, bitCounts)
        if ret != 0:
            # 重新试一次
            gevent.sleep(0.1)
            ret, val = self.get_modbus_bit(bitPos, bitCounts)
            if ret != 0:
                self.errorMsg = "in is_stop_state() 读取 modbus_bit ({}) 失败".format(bitPos)
                return -1, self.errorMsg
        return 0, val[0]

    def is_rs_stop(self):
        """
        获取上料控制器是否是急停状态
        :return:
        """
        bitPos = 102
        bitCounts = 1
        ret, val = self.get_modbus_bit(bitPos, bitCounts)
        if ret != 0:
            # 重新试一次
            gevent.sleep(0.1)
            ret, val = self.get_modbus_bit(bitPos, bitCounts)
            if ret != 0:
                self.errorMsg = "in is_rs_stop() 读取 modbus_bit ({}) 失败".format(bitPos)
                return -1, self.errorMsg
        return 0, val[0]

    def is_reset_state(self):
        """
        获取上料控制器是否是复位状态
        :return:
        """
        bitPos = 103
        bitCounts = 1
        ret, val = self.get_modbus_bit(bitPos, bitCounts)
        if ret != 0:
            # 重新试一次
            gevent.sleep(0.1)
            ret, val = self.get_modbus_bit(bitPos, bitCounts)
            if ret != 0:
                self.errorMsg = "in is_reset_state() 读取 modbus_bit ({}) 失败".format(bitPos)
                return -1, self.errorMsg
        return 0, val[0]

    def close_door_alarm(self):
        """
        关闭门报警
        :return:
        """
        bitPos = 200
        bitValue = 1
        ret = self.set_modbus_bit(bitPos, bitValue)
        if ret != 0:
            # 重新试一次
            gevent.sleep(self.tryAgainDuraion)
            ret = self.set_modbus_bit(bitPos, bitValue)
            if ret != 0:
                self.errorMsg = "in close_door_alarm() 设置 modbus_bit({}) = {} 失败".format(bitPos, bitValue)
                return self.errorMsg
        return ret

    def open_door_alarm(self):
        """打开门报警"""
        bitPos = 200
        bitValue = 0
        ret = self.set_modbus_bit(bitPos, bitValue)
        if ret != 0:
            # 重新试一次
            gevent.sleep(self.tryAgainDuraion)
            ret = self.set_modbus_bit(bitPos, bitValue)
            if ret != 0:
                self.errorMsg = "in open_door_alarm() 设置 modbus_bit({}) = {} 失败".format(bitPos, bitValue)
                return self.errorMsg
        return ret

    def light_source_handle(self, isOpen):
        """
        光源控制,传入true或者false,如果是true,就是打开光源,如果是false就是关闭光源
        :param isOpen:
        :return:
        """
        bitPos = 6
        bitValue = 1 if isOpen else 0
        ret = self.set_output(bitPos, bitValue)
        if ret == -1:
            gevent.sleep(self.tryAgainDuraion)
            ret = self.set_output(bitPos, bitValue)
            if ret == -1:
                return self.errorMsg
        return 0

    def set_table(self, start, num, table):
        """
        将数据写入到寄存器的表中
        :param start:表格的起始位置
        :param num:写入的值个数
        :param table:写入的值列表
        :return:
        """
        value = (c_float * num)(0)
        value[:] = table
        self.aux_api.ZAux_Direct_SetTable.restype = c_int32
        self.aux_api.ZAux_Direct_SetTable.argtype = (c_void_p, c_uint32, c_uint32, c_void_p)

        try:
            res = self.aux_api.ZAux_Direct_SetTable(self.handleRobot, c_int32(start), c_int32(num), value)
        except Exception as e:
            res = -1
            errorInfo = "in Zmc(set_table)  error: {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)
        if res != 0:
            self.errorMsg = "in set_table() ZAux_Direct_GetTable() 执行失败"
        return res

    def get_table(self, start, num):
        """
        从底层的表中读取数据table表
        :param start: 读取的起始位置
        :param num: 读取的数据表的个数
        :return:
        """
        value = (c_float * num)()
        self.aux_api.ZAux_Direct_GetTable.restype = c_int32
        self.aux_api.ZAux_Direct_GetTable.argtype = (c_void_p, c_int32, c_int32, c_void_p)
        try:
            res = self.aux_api.ZAux_Direct_GetTable(self.handleRobot, c_int32(start), c_int32(num), value)
        except Exception as e:
            res = -1
            errorInfo = "in CamZmc.get_table() error : {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)
        if res != 0:
            self.errorMsg = "in CamZmc.get_table() ZAux_Direct_GetTable 执行失败"

        return res, value[:]

    def get_output(self, portPos):
        """
        获取输出口的状态
        :param portPos:
        :return: 结果码:0后者其他 值也是0或者1
        """
        value = (c_uint32 * 1)()
        self.aux_api.ZAux_Direct_GetOp.restype = c_int32
        self.aux_api.ZAux_Direct_GetOp.argtype = (c_void_p, c_int32, c_void_p)
        try:
            res = self.aux_api.ZAux_Direct_GetOp(self.handleRobot, c_int32(portPos), value)
        except Exception as e:
            res = -1
            errorInfo = "in get_output() error : {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)

        if res != 0:
            self.errorMsg = "in get_output(): ZAux_Direct_GetOp() 执行失败"
        return res, value[:][0]

    def set_output(self, port, value):
        """
        输出口控制
        :param port:
        :param value:
        :return:
        """
        self.aux_api.ZAux_Direct_SetOp.restype = c_int32
        self.aux_api.ZAux_Direct_SetOp.argtype = (c_void_p, c_int32, c_uint32)
        try:
            res = self.aux_api.ZAux_Direct_SetOp(self.handleRobot, c_int32(port), c_uint32(value))
        except Exception as e:
            res = -1
            errorMsg = "in set_output() error: {}".format(str(e))
            print(errorMsg)
            self.logger.debug(errorMsg)
        if res != 0:
            self.errorMsg = "in set_output(): 输出口设置失败"
        return res

    def set_release_board_delay(self, isDelay):
        """
        设置是否要停板的标志位,如果是长铜箔isDelay等于True,就设置为1,如果isDelay等于False,就设置为0.
        :return:
        """
        bitPos = 31
        bitValue = 1 if isDelay else False
        ret = self.set_modbus_bit(bitPos, bitValue)
        if ret != 0:
            # 重新试一次
            gevent.sleep(0.1)
            ret = self.set_modbus_bit(bitPos, bitValue)
            if ret != 0:
                self.err_msg = "in camZmc.set_release_board_delay() set camZmc modbusbit({}) = {} 失败".format(bitPos, bitValue)
                return self.err_msg
        return ret

    def connect_zmc(self, ip):
        """
        拍照控制器连接接口
        :param ip 设备的IP地址
        :return:
        """
        if isinstance(ip, type(" ")):
            ip = ip.encode('ascii')
        self.api.ZMC_OpenEth.restype = c_int64
        self.api.ZMC_OpenEth.argtype = (c_char_p, c_void_p)
        status = self.api.ZMC_OpenEth(ip, byref(self.handleRobot))
        if status == 0:
            self.errorMsg = ""
        else:
            self.errorMsg = "拍照控制器连接失败,{}".format(status)
        return status

    def close_zmc_conn(self):
        """
        关闭zmc连接
        :return:
        """
        self.api.ZMC_Close.restype = c_int32
        self.api.ZMC_Close.argtype = c_void_p
        status = self.api.ZMC_Close(self.handleRobot)
        return status

    def set_modbus_bit(self, pos, value):
        """
        设置modbus的位寄存器
        :param pos: 设置的bit位位置
        :param value:设置的值
        :return:
        """
        value = (c_uint8 * 1)(value)
        self.aux_api.ZAux_Modbus_Set0x.restype = c_int32
        self.aux_api.ZAux_Modbus_Set0x.argtype = (c_void_p, c_uint16, c_uint16, c_void_p)

        try:
            res = self.aux_api.ZAux_Modbus_Set0x(self.handleRobot, c_uint16(pos), c_uint16(1), value)
        except Exception as e:
            res = -1
            print("in Zmc(set_modbus_bit)  error: {}".format(e))
            self.logger.debug("in  Zmc(set_modbus_bit), error: {}".format(e))

        if res != 0:
            self.errorMsg = "in set_modbus_bit() ZAux_Modbus_Set0x() 执行失败"

        return res

    def get_modbus_bit(self, start, num):
        """
        获取modbus_bit的bit位的值,返回一个列表,里面存放的是所有的bit位的结果值.
        :param start:获取的bit位的起始位置
        :param num:获取的bit位的个数
        :return:
        """
        value = (c_uint8 * num)(0)
        self.aux_api.ZAux_Modbus_Get0x.restype = c_int32
        self.aux_api.ZAux_Modbus_Get0x.argtype = (c_void_p, c_uint16, c_uint16, c_void_p)

        try:
            res = self.aux_api.ZAux_Modbus_Get0x(self.handleRobot, c_uint16(start), c_uint16(num), value)
        except Exception as e:
            res = -1
            errorInfo = "in get_modbus_bit() error:{}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)

        if res != 0:
            self.errorMsg = "in get_modbus_bit() ZAux_Modbus_Get0x() 执行失败"
        return res, value[:]

    def get_axis_postion(self, axisCounts):
        """
         获取轴的坐标,从0轴开始
        :param axisCounts:轴的个数
        :return:返回轴的坐标的值
        """
        value = (c_float * axisCounts)()
        self.aux_api.ZAux_GetModbusDpos.restype = c_int32
        self.aux_api.ZAux_GetModbusDpos.argtype = (c_void_p, c_int32, c_void_p)
        try:
            res = self.aux_api.ZAux_GetModbusDpos(self.handleRobot, c_int32(axisCounts), value)
        except Exception as e:
            res = -1
            errorInfo = "in CamZmc.get_axis_postion() error:{}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)

        if res != 0:
            self.errorMsg = "in CamZmc.get_axis_postion(): ZAux_GetModbusDpos() 执行失败"

        return res, value[:]

    def set_modbus_byte(self, bytePos, value):
        """
        设置modbus_byte字节寄存器
        :param bytePos:
        :param value:
        :return:
        """
        value = (c_uint16 * 1)(value)
        self.aux_api.ZAux_Modbus_Set4x.restype = c_int32
        self.aux_api.ZAux_Modbus_Set4x.argtype = (c_void_p, c_uint16, c_uint16, c_void_p)

        try:
            res = self.aux_api.ZAux_Modbus_Set4x(self.handleRobot, c_uint16(bytePos), c_uint16(1), value)
        except Exception as e:
            res = -1
            errorInfo = "in CamZmc.set_modbus_byte() error : {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)
        if res != 0:
            self.errorMsg = "in CamZmc.set_modbus_byte() ZAux_Modbus_Set4x 执行失败"

        return res

    def set_modbus_float(self, start, counts, values):
        """
        设置modbus_ieee()浮点数的额寄存器,可以设置number个数,然后value
        :param start:modbus_ieee的起点
        :param counts:设置的个数
        :param values:设置的结果值
        :return:
        """
        values = (c_float * counts)(*values)
        self.aux_api.ZAux_Modbus_Set4x_Float.restype = c_int32
        self.aux_api.ZAux_Modbus_Set4x_Float.argtype = (c_void_p, c_uint16, c_uint16, c_void_p)

        try:
            res = self.aux_api.ZAux_Modbus_Set4x_Float(self.handleRobot, c_uint16(start), c_uint16(counts), values)
        except Exception as e:
            res = -1
            errorInfo = "in CamZmc.set_modbus_float(),error: {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)
        if res != 0:
            self.errorMsg = "in CamZmc.set_modbus_float() ZAux_Modbus_Set4x_Float() 执行失败"

        return res

    def get_modbus_float(self, start, counts):
        """
        获取modbus_ieee()的值,可以获取多个值
        :param start: modbus_ieee起始位置
        :param counts: 读取的数量
        :return:
        """
        value = (c_float * counts)()
        self.aux_api.ZAux_Modbus_Get4x_Float.restype = c_int32
        self.aux_api.ZAux_Modbus_Get4x_Float.argtype = (c_void_p, c_uint16, c_uint16, c_void_p)

        try:
            res = self.aux_api.ZAux_Modbus_Get4x_Float(self.handleRobot, c_uint16(start), c_uint16(counts), value)
        except Exception as e:
            res = -1
            print(e)

        if res != 0:
            self.errorMsg = "ZAux_Modbus_Get4x_Float 执行失败"
        return res, value[:]

    def excute_zmc_cmd(self, cmd):
        """
        控制执行指令,跟用zdveloper的命令行获取的cmd命令状态是一样的效果
        :param cmd:输入的命令指示
        :return:
        """
        res = b" " * 256
        cmd = cmd.encode('ascii')
        self.api.ZMC_Execute.restype = c_uint32
        self.api.ZMC_Execute.argtype = (c_void_p, c_char_p, c_uint32, c_char_p, c_uint32)
        try:
            value = self.api.ZMC_Execute(self.handleRobot, cmd, 1000, res, 256)
        except Exception as e:
            errorInfo = "in CamZmc.excute_zmc_cmd() error : {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)
            value = -1
        if value != 0:
            self.errorMsg = "in CamZmc.excute_zmc_cmd() ZMC_Execute() 执行失败"
        return value, res.strip()[:-2]

    def move_abs_four_axis(self, movePos):
        """
        四轴绝对运动,同时移动四个轴,在急停状态下不能移动
        :param movePos 移动到的绝对坐标位置
        :return:
        """
        ret = self.is_rs_stop()
        if ret == 1:
            return "设备急停,请先复位设备"
        self.excute_zmc_cmd("base(0,1,2,3)")
        cmd = "moveabs(" + str("%.3f" % movePos[0]) + "," + str("%.3f" % movePos[1]) + "," + str("%.3f" % movePos[2]) + "," + str(
            "%.3f" % movePos[3]) + ")"
        return self.excute_zmc_cmd(cmd)

    def move_x(self, dis):
        """
        上料移动X轴
        :param dis:移动距离
        :return:
        """
        ret = self.is_rs_stop()
        if ret == 1:
            return "设备急停,请先复位设备"
        self.excute_zmc_cmd("base(0)")
        cmd = "pmove(" + str("%.3f" % dis) + ")"
        return self.excute_zmc_cmd(cmd)

    def move_y(self, dis):
        """
        上料移动Y轴
        :param dis:移动距离
        :return:
        """
        ret = self.is_rs_stop()
        if ret == 1:
            return "设备急停,请先复位设备"
        self.excute_zmc_cmd("base(1)")
        cmd = "pmove(" + str("%.3f" % dis) + ")"
        return self.excute_zmc_cmd(cmd)

    def move_z(self, dis):
        """
        上料移动Z轴
        :param dis:移动距离
        :return:
        """
        ret = self.is_rs_stop()
        if ret == 1:
            return "设备急停,请先复位设备"
        self.excute_zmc_cmd("base(2)")
        cmd = "pmove(" + str("%.3f" % dis) + ")"
        return self.excute_zmc_cmd(cmd)

    def move_w(self, dis):
        """
        上料移动W轴
        :param dis:移动距离一般是角度
        :return:
        """
        ret = self.is_rs_stop()
        if ret == 1:
            return "设备急停,请先复位设备"
        self.excute_zmc_cmd("base(3)")
        cmd = "pmove(" + str("%.3f" % dis) + ")"
        return self.excute_zmc_cmd(cmd)

    def move_abs_x(self, dis):
        """
        上料移动X轴,绝对移动
        :param dis:移动距离
        :return:
        """
        ret = self.is_rs_stop()
        if ret == 1:
            return "设备急停,请先复位设备"
        self.excute_zmc_cmd("base(0)")
        cmd = "moveabs(" + str("%.3f" % dis) + ")"
        return self.excute_zmc_cmd(cmd)

    def move_abs_y(self, dis):
        """
        上料移动Y轴,绝对移动
        :param dis:移动距离
        :return:
        """
        ret = self.is_rs_stop()
        if ret == 1:
            return "设备急停,请先复位设备"
        self.excute_zmc_cmd("base(1)")
        cmd = "moveabs(" + str("%.3f" % dis) + ")"
        return self.excute_zmc_cmd(cmd)

    def move_abs_z(self, dis):
        """
        上料移动Z轴,绝对移动
        :param dis:移动距离
        :return:
        """
        ret = self.is_rs_stop()
        if ret == 1:
            return "设备急停,请先复位设备"
        self.excute_zmc_cmd("base(2)")
        cmd = "moveabs(" + str("%.3f" % dis) + ")"
        return self.excute_zmc_cmd(cmd)

    def move_abs_w(self, dis):
        """
        上料移动W轴,绝对移动
        :param dis:移动距离一般是角度
        :return:
        """
        ret = self.is_rs_stop()
        if ret == 1:
            return "设备急停,请先复位设备"
        self.excute_zmc_cmd("base(3)")
        cmd = "moveabs(" + str("%.3f" % dis) + ")"
        return self.excute_zmc_cmd(cmd)

    def set_modbus_string(self, msg):
        """
        可以发送字符串,后期用来备用
        :param msg:发送的消息
        :return:
        """
        maxStrNum = 12
        newMsg = msg
        if len(msg) < maxStrNum:
            n = int((maxStrNum - len(msg)) // 2)
            newMsg = " " * n + msg
        for i in range(0, maxStrNum + 1, 2):
            try:
                b = newMsg[i]
            except Exception as e:
                errorInfo = "in set_modbus_string() error : {}".format(str(e))
                print(errorInfo)
                b = " "
            try:
                a = newMsg[i + 1]
            except Exception as e:
                errorInfo = "in set_modbus_string() error : {}".format(str(e))
                print(errorInfo)
                a = " "

            self.set_modbus_byte(1010 + int(i / 2), (ord(a) << 8) | ord(b))

    def reconnect_cam_zmc(self):
        """
        重新连接上料控制器,直接在camZmc对象中进行连接,不要再写到produce中去了,它的连接它自己负责就可以了.
        :return:
        """
        try:
            self.close_zmc_conn()
        except Exception as e:
            errorInfo = "in CamZmc.reconnect_cam_zmc() error: {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)

        status = self.connect_zmc(self.ip)
        if status == 0:
            # 连接成功以后再下发参数
            self.logger.debug("上料控制器连接成功.")
            res, info = self.set_cam_zmc_params_to_device()
            if res == -1:
                return "error", info
        return "ok" if status == 0 else self.errorMsg

    def set_cam_zmc_params_to_device(self):
        """
        将拍照控制器的参数下发到底层
        :return:
        """
        try:
            res, paramsDict = self.db.get_cam_zmc_params(self.tableName)
            if res != "ok":
                return "error", paramsDict
            else:
                paramsDict.pop("key")
                if len(paramsDict.values()) == 0:
                    info = "数据库中无数据,先从底层获取拍照控制器的参数数据"
                    print(info)
                    self.logger.debug(info)
                    return self.write_cam_zmc_params_to_mogodb()
                else:
                    paramsList = self.get_cam_zmc_params_value_list_by_dict(paramsDict)
                    if paramsList is None:
                        info = "上料控制器下发参数失败,里面有None值"
                        print(info)
                        self.logger.debug(info)
                    else:
                        # 设置参数到拍照控制器,过滤掉当量位置.当量位置过滤出来.[2,32,60,82]
                        list1 = paramsList[:1]
                        list2 = paramsList[2:16]
                        list3 = paramsList[17:30]
                        list4 = paramsList[31:41]
                        list5 = paramsList[42:]
                        ret1 = self.set_modbus_float(0, len(list1), list1)
                        if ret1 != 0:
                            gevent.sleep(0.5)
                            ret1 = self.set_modbus_float(0, len(list1), list1)

                        ret2 = self.set_modbus_float(4, len(list2), list2)
                        if ret2 != 0:
                            gevent.sleep(0.5)
                            ret2 = self.set_modbus_float(4, len(list2), list2)

                        ret3 = self.set_modbus_float(34, len(list3), list3)
                        if ret3 != 0:
                            gevent.sleep(0.5)
                            ret3 = self.set_modbus_float(34, len(list3), list3)

                        ret4 = self.set_modbus_float(62, len(list4), list4)
                        if ret4 != 0:
                            gevent.sleep(0.5)
                            ret4 = self.set_modbus_float(62, len(list4), list4)

                        ret5 = self.set_modbus_float(84, len(list5), list5)
                        if ret5 != 0:
                            ret5 = self.set_modbus_float(84, len(list5), list5)

                        # 如果写入失败就报错,并且返回-1
                        if any([ret1, ret2, ret3, ret4, ret5]):
                            errorInfo = "in CamZmc.set_cam_zmc_params_to_devie() error: 拍照控制器参数下发失败"
                            print(errorInfo)
                            self.logger.debug(errorInfo)
                            return -1, errorInfo
                        else:
                            okInfo = "上料控制器参数下发成功!"
                            print(okInfo)
                            self.logger.debug(okInfo)
                            return 0, "ok"

        except Exception as e:
            errorInfo = "in CamZmc.set_cam_zmc_params_to_devie() error: {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)
            return -1, errorInfo

    def write_cam_zmc_params_to_mogodb(self):
        """
        将拍照控制器的参数,写入到mongodb数据库中.写入的时候,注意要先将小数的部分取后3位即可,不要搞的小数点那么多.
        :return:
        """
        ret, val = self.get_modbus_float(0, 51)
        if ret != 0:
            return "error", "in CamZmc.write_cam_zmc_params_to_mogodb() error: 读取底层数据失败!"
        else:
            # 1.先将val里面的数据转换一下,避免有小数点位数太多的数据
            camZmcParamsValue = [round(x, 3) for x in val]
            camZmcParams = dict(zip(camZmcParamsKey, camZmcParamsValue))
            query = {
                "key": "1"
            }
            newValues = {
                "$set": camZmcParams
            }
            return self.db.update_data(self.tableName, query, newValues)

    @staticmethod
    def get_cam_zmc_params_value_list_by_dict(paramsDict):
        """
        根据参数字典的键值列表获取对应的key对应的value列表,为什么不直接用dict的value,是因为dict的value是无序的,key也是无序的,
        不能保证key刚好对应了设备的value值.
        :return:
        """
        paramsList = [paramsDict.get(key) for key in camZmcParamsKey]
        if None in paramsList:
            return None
        else:
            return paramsList

    def web_request_get_cam_zmc_params(self):
        """
        前端界面请求上料的参数
        :return:
        """
        try:
            res, paramsDict = self.db.get_cam_zmc_params(self.tableName)
            if res != "ok":
                return "数据库错误,获取的数据为空"
            else:
                return [paramsDict]
        except Exception as e:
            errorInfo = "in CamZmc.web_request_get_cam_zmc_params() error: {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)
            return errorInfo

    def web_request_save_cam_zmc_params(self, newParams):
        """
        界面上点击了保存上料控制器的参数
        :param newParams:新的参数
        :return:
        """
        try:
            query = {
                "key": "1"
            }
            newValues = {
                "$set": newParams
            }
            res, info = self.db.update_data(self.tableName, query, newValues)
            connected = self.check_connect()
            if connected:  # 如果设备是连接状态,再下发参数
                res, info = self.set_cam_zmc_params_to_device()
                if res == -1:
                    return info
            return "ok" if res == "ok" else info
        except Exception as e:
            errorInfo = "in CutterZmc.web_request_save_cutter_zmc_params() error: {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)
            return errorInfo

    def web_request_start_cam_zmc(self):
        """
        前端界面点击了启动上料设备,单独启动
        :return:
        """
        ret = self.cam_zmc_start()
        return "ok" if ret == 0 else ret

    def web_request_stop_cam_zmc(self):
        """
        前端界面点击了停止上料设备,单独停止
        :return:
        """
        ret = self.cam_zmc_rs_stop()  # 直接急停,它的停止不管用
        return "ok" if ret == 0 else ret

    def web_request_reset_cam_zmc(self):
        """
        前端界面上点击了单独复位设备,单独复位
        :return:
        """
        gevent.sleep(1)
        ret = self.cam_zmc_reset()
        return "ok" if ret == 0 else ret

    def get_In(self, portPos):
        """
        获取输出口的状态
        :param portPos:
        :return: 结果码:0后者其他 值也是0或者1
        """
        value = (c_uint32 * 1)()
        self.aux_api.ZAux_Direct_GetOp.restype = c_int32
        self.aux_api.ZAux_Direct_GetOp.argtype = (c_void_p, c_int32, c_void_p)
        try:
            res = self.aux_api.ZAux_Direct_GetIn(self.handleRobot, c_int32(portPos), value)
        except Exception as e:
            res = -1
            errorInfo = "in get_output() error : {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)

        if res != 0:
            self.errorMsg = "in get_output(): ZAux_Direct_GetOp() 执行失败"
        return res, value[:][0]

    def set_layout_mode(self, layoutMode):
        """
        设置正常排版模式
        """
        normalBit = 36
        rotateBit = 37
        if layoutMode == "竖排":
            normalVal = 1
            rotateVal = 0
        else:
            normalVal = 0
            rotateVal = 1
        ret1 = self.set_modbus_bit(normalBit, normalVal)
        if ret1 != 0:
            # 重新试一次
            gevent.sleep(self.tryAgainDuraion)
            ret1 = self.set_modbus_bit(normalBit, normalVal)
            if ret1 != 0:
                self.errorMsg = "in CutterZmc.set_single_board_joint_flag() 设置  modbus_bit({}) = {} 失败".format(normalBit, normalVal)
                return self.errorMsg

        ret2 = self.set_modbus_bit(rotateBit, rotateVal)
        if ret2 != 0:
            # 重新试一次
            gevent.sleep(self.tryAgainDuraion)
            ret2 = self.set_modbus_bit(rotateBit, rotateVal)
            if ret2 != 0:
                self.errorMsg = "in CutterZmc.set_single_board_joint_flag() 设置 modbus_bit ({}) 失败".format(rotateBit, rotateVal)
                return self.errorMsg
        return 0

    # 获取输入口的状态
    def get_in_state_multi(self, start, count):
        """
        获取多少个输入口的状态,返回的是一个列表,列表里是一个32位的整数,这个整数的每一位表示一个值.
        所以这里返回值也是一个列表,列表的大小就是end-start. 这个值怎么获取呢,就是通过将整数转换为二进制的数即可.
        """
        list_count = int(count / 32 + 0.5)  # 返回的值的个数, 是count有多少个32
        res_list_val = [0] * count  # 默认的返回值
        value = (c_int32 * list_count)()
        self.aux_api.ZAux_Direct_GetInMulti.restype = c_int32
        self.aux_api.ZAux_Direct_GetInMulti.argtype = (c_void_p, c_int32, c_int32, c_void_p)
        try:
            res = self.aux_api.ZAux_Direct_GetInMulti(self.handleRobot, c_int32(start), c_int32(start + count), value)
        except Exception as e:
            res = -1
            errorInfo = "in get_in_state_multi() error : {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)

        if res != 0:
            self.errorMsg = "in get_in_state_multi(): ZAux_Direct_GetInMulti() 执行失败"
            return res, []
        # 将返回值的值先转换为bin类型
        for index, val_int in enumerate(value[:]):
            val_bins = bin(val_int).replace('0b', '')  # 这里是字符串,并且返回的是倒叙的
            val_bin_list = list(val_bins)
            val_bin_list = val_bin_list[::-1]
            for indexinner, valinner in enumerate(val_bin_list):
                res_list_val[indexinner + 32 * index] = valinner
        return res, res_list_val


if __name__ == '__main__':
    camZmc = CamZmc()
    isOk = camZmc.reconnect_cam_zmc()
    print("connectOk = {}".format(isOk))
    get_in_is_ok, vals = camZmc.get_in_state_multi(0, 64)
    print("get_In(17) = {}, {}".format(isOk, vals))
